package com.asambleacristiana.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

import org.apache.commons.lang3.StringUtils;

import com.asambleacristiana.domain.Cuenta;
import com.asambleacristiana.domain.CuentaEnum;
import com.asambleacristiana.domain.MesesEnum;
import com.asambleacristiana.domain.Movimiento;
import com.asambleacristiana.domain.TipoComprobanteEnum;
import com.asambleacristiana.domain.TipoOperacion;
import com.asambleacristiana.domain.TipoOperacionEnum;
import com.asambleacristiana.domain.Zona;
import com.asambleacristiana.repository.dao.DAOFactoryJPA;
import com.asambleacristiana.utils.Commons;
import com.asambleacristiana.utils.FechaUtils;

@ManagedBean
@ViewScoped
public class AdminMovimientosBean extends Commons implements Serializable {

	private static final long serialVersionUID = 1L;

	private Date fecha;
	private Integer cuentaSelec = null;
	private Integer anioSelec = null;
	private Integer codZonaActual = null;
	private Double importe;
	private Double importeTotal;
	private Double importeTotalRestante;
	private List<Cuenta> cuentas = null;
	private List<Zona> zonasAsociadas = null;
	private List<Movimiento> movimientos = null;
	private Integer tipoOpSelec = null;
	private boolean botonConfirmarHabilitado = false;
	private List<TipoOperacion> tipoOperaciones = null;
	private int movimientoIndex;
	private String[] detalles = { "Ofrenda de la fecha.", "Recaudacion de la fecha", "Transporte: saldo contable del mes anterior" };
	private String detalle;
	private String nroFactura1;
	private String nroFactura2;
	private TipoComprobanteEnum tipoFactura;
	private String cuit;
	private List<MovimientoFondoComun> movimientosAlFondoComun;

	private int mesSelec;

	@PostConstruct
	public void postContruct() {
		tipoOperaciones = DAOFactoryJPA.getInstance().getTipoOperacionDAO().findAll();
		TipoOperacion operacion = tipoOperaciones.get(0);
		tipoOpSelec = operacion.getCodigo();
		cuentas = operacion.getCuentas();
		zonasAsociadas = getUsuarioLogueado().getZonaSeleccionada().getZonasAsociadas();
		movimientos = new ArrayList<Movimiento>();
		anioSelec = Calendar.getInstance().get(Calendar.YEAR);
		generarMovimientos(anioSelec,Calendar.JANUARY);
	}

	public SelectItem[] getMeses() {
		SelectItem[] items = new SelectItem[12];
		int i = 0;
		for (MesesEnum m : MesesEnum.values()) {
			items[i++] = new SelectItem(m.getCodigo(), m.name());
		}
		return items;
	}

	public SelectItem[] getAnios() {
		SelectItem[] items = new SelectItem[3];
		int anio =  Calendar.getInstance().get(Calendar.YEAR);
		items[0] = new SelectItem(anio, String.valueOf(anio));
		for (int j = 1; j < 3; j++) {
			anio =  anio - 1;
			items[j] = new SelectItem(anio, String.valueOf(anio));
		}
		return items;
	}
	
	
	public SelectItem[] getTiposFacturas() {
		SelectItem[] items = new SelectItem[TipoComprobanteEnum.values().length];
		int i = 0;
		for (TipoComprobanteEnum g : TipoComprobanteEnum.values()) {
			items[i++] = new SelectItem(g, g.getTipo());
		}
		return items;
	}

	public void tipoOperacionChanged(ValueChangeEvent e) {
		tipoOpSelec = (Integer) e.getNewValue();
		cuentas = DAOFactoryJPA.getInstance().getTipoOperacionDAO().findById(tipoOpSelec).getCuentas();
		limpiarCampos();
	}

	public void mesChanged(ValueChangeEvent e) {
		int mes = (Integer) e.getNewValue();
		generarMovimientos(anioSelec,mes);
	}
	

	public void anioChanged(ValueChangeEvent e) {
		int anio = (Integer) e.getNewValue();
		generarMovimientos(anio,mesSelec);
	}

	public int getMesSelec() {
		return mesSelec;
	}

	public void setMesSelec(int mesSelec) {
		this.mesSelec = mesSelec;
	}

	private void generarMovimientos(int anio,int mes) {
		movimientosAlFondoComun = new ArrayList<MovimientoFondoComun>();
		fecha = new Date();
		importeTotal=0D;
		importeTotalRestante=0D;
		for (Zona zona : getUsuarioLogueado().getAllZonas()) {
			MovimientoFondoComun fondoComun = new MovimientoFondoComun(anio,mes, zona, getUsuarioLogueado().getApodo());
			fondoComun.setRegistrado(DAOFactoryJPA.getInstance().getMovimientoDAO().find(fondoComun.getEgreso()));
			if (fondoComun.getImporteTotalMes() > 0) {
				movimientosAlFondoComun.add(fondoComun);
				importeTotal += fondoComun.getAporteMes();
				if (!fondoComun.isRegistrado()) {
					importeTotalRestante += fondoComun.getAporteMes();
				}
			}
		}

	}

	public List<MovimientoFondoComun> getMovimientosAlFondoComun() {
		return movimientosAlFondoComun;
	}

	public void setMovimientosAlFondoComun(List<MovimientoFondoComun> movimientosAlFondoComun) {
		this.movimientosAlFondoComun = movimientosAlFondoComun;
	}

	public void doAgregarMovimiento() {

//		if (FechaUtils.isFechaValida(fecha)|| isSuperUsu()) {
			Cuenta cuenta = DAOFactoryJPA.getInstance().getCuentaDAO().findById(cuentaSelec);
			TipoOperacion tipoOperacion = DAOFactoryJPA.getInstance().getTipoOperacionDAO().findById(tipoOpSelec);
			Zona zonaActual = DAOFactoryJPA.getInstance().getZonaDAO().findById(codZonaActual);

			Cuenta desde = null;
			Cuenta hasta = null;
			Cuenta caja = DAOFactoryJPA.getInstance().getCuentaDAO().findById(CuentaEnum.caja.getCodigo());

			if (tipoOperacion.getCodigo().equals(TipoOperacionEnum.ingreso.getCodigo())) {
				desde = cuenta;
				hasta = caja;
				limpiarCampos();
			} else {
				limpiarTipoDeFactura();
				desde = caja;
				hasta = cuenta;
			}

			if (TipoComprobanteEnum.A.equals(tipoFactura) || TipoComprobanteEnum.ReciboA.equals(tipoFactura)
					|| TipoComprobanteEnum.NotaDeCreditoA.equals(tipoFactura)) {
				mensajeFacturaA();
			}

			Movimiento movimiento = crearMovimiento(tipoOperacion, zonaActual, desde, hasta);

			if (!movimientos.contains(movimiento)) {
				movimientos.add(movimiento);
			} else {
				mensajeMovimientoEnGrilla();
			}
			cambiarEstadoBoton();
//		} else {
//			FacesContext.getCurrentInstance().addMessage(
//					null,
//					new FacesMessage(FacesMessage.SEVERITY_ERROR, "No se permiten ingresar movimientos fuera de término.",
//							"No se permiten ingresar movimientos fuera de término."));
//		}
	}

	private void mensajeMovimientoEnGrilla() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR, "El movimiento que intenta ingresar ya existe en la grilla.",
						"El movimiento que intenta ingresar ya existe en la grilla."));
	}

	private Movimiento crearMovimiento(TipoOperacion tipoOperacion, Zona zonaActual, Cuenta desde, Cuenta hasta) {
		Movimiento movimiento = new Movimiento(zonaActual, fecha, tipoOperacion, desde, hasta, importe, detalle, getUsuarioLogueado().getApodo(), tipoFactura,
				getNroFactura(nroFactura1, nroFactura2), cuit);
		return movimiento;
	}

	private void mensajeFacturaA() {
		FacesContext
				.getCurrentInstance()
				.addMessage(
						null,
						new FacesMessage(
								FacesMessage.SEVERITY_ERROR,
								"Atención: Corresponde un comprobante \"B\" al ser (nosotros) \"IVA EXENTO\". EL comprobante \"A\" es"
										+ " para los que discriminan IVA (\"IVA INSCRIPTO\"). Procurar sustituir el comprobante por el correcto (\"B\") y que no repitan el error.",
								""));
	}

	private String getNroFactura(String nroFac1, String nroFac2) {
		String nroFactura = null;
		if (!StringUtils.isBlank(nroFac1) && !StringUtils.isBlank(nroFac2)) {
			nroFactura = nroFactura1 + nroFactura2;
		}
		return nroFactura;
	}

	private void limpiarTipoDeFactura() {
		if (StringUtils.isBlank(nroFactura1) || StringUtils.isBlank(nroFactura2)) {
			tipoFactura = null;
		}
	}

	public void doConfirmarMovimientos() {
		for (Iterator<Movimiento> iterator = movimientos.iterator(); iterator.hasNext();) {
			Movimiento movimiento = iterator.next();
			boolean existe = DAOFactoryJPA.getInstance().getMovimientoDAO().find(movimiento);
			if (!existe) {
				DAOFactoryJPA.getInstance().getMovimientoDAO().persist(movimiento);
				iterator.remove();
			}
		}
		if (movimientos.size() == 0) {
			cambiarEstadoBoton();
		} else {
			mensajeMovimientoEnBD();
		}
	}

	public void doConfirmarMovimientosFondoComun() {
		int mes = 0;
		for (Iterator<MovimientoFondoComun> iterator = movimientosAlFondoComun.iterator(); iterator.hasNext();) {
			MovimientoFondoComun fondoComun = iterator.next();
			mes = fondoComun.getMes();
			if (!fondoComun.isRegistrado()) {// la ingreso solo si no existe
				DAOFactoryJPA.getInstance().getMovimientoDAO().persist(fondoComun.getEgreso());
				DAOFactoryJPA.getInstance().getMovimientoDAO().persist(fondoComun.getIngreso());
			}
		}
		generarMovimientos(anioSelec,mes);
	}

	private void mensajeMovimientoEnBD() {
		FacesContext.getCurrentInstance().addMessage(
				null,
				new FacesMessage(FacesMessage.SEVERITY_ERROR,
						"Los movimientos que intenta ingresar ya se encuentran registrados en la base de datos. Por favor verificarlos.",
						"Los movimientos que intenta ingresar ya se encuentran registrados en la base de datos. Por favor verificarlos."));
	}

	public void doBorrar() {
		movimientos.remove(movimientoIndex);
		cambiarEstadoBoton();
	}

	public void limpiarCampos() {
		nroFactura1 = null;
		nroFactura2 = null;
		cuit = null;
		tipoFactura = null;
		if (tipoOpSelec.equals(TipoOperacionEnum.egreso.getCodigo())) {
			tipoFactura = TipoComprobanteEnum.C;
		}
	}

	private void cambiarEstadoBoton() {
		botonConfirmarHabilitado = true;
		if (movimientos.isEmpty()) {
			botonConfirmarHabilitado = false;
		}
	}

	public List<Movimiento> getMovimientos() {
		return movimientos;
	}

	public void setMovimientos(List<Movimiento> movimientos) {
		this.movimientos = movimientos;
	}

	public Double getImporte() {
		return importe;
	}

	public void setImporte(Double importe) {
		this.importe = importe;
	}

	public Integer getCuentaSelec() {
		return cuentaSelec;
	}

	public void setCuentaSelec(Integer cuentaSelec) {
		this.cuentaSelec = cuentaSelec;
	}

	public int getMovimientoIndex() {
		return movimientoIndex;
	}

	public void setMovimientoIndex(int movimientoIndex) {
		this.movimientoIndex = movimientoIndex;
	}

	public boolean isBotonConfirmarHabilitado() {
		return botonConfirmarHabilitado;
	}

	public void setBotonConfirmarHabilitado(boolean botonConfirmarHabilitado) {
		this.botonConfirmarHabilitado = botonConfirmarHabilitado;
	}

	public String getDetalle() {
		return detalle;
	}

	public void setDetalle(String detalle) {
		this.detalle = detalle;
	}

	public String[] getDetalles() {
		return detalles;
	}

	public void setDetalles(String[] detalles) {
		this.detalles = detalles;
	}

	public void setZonasAsociadas(List<Zona> zonasAsociadas) {
		this.zonasAsociadas = zonasAsociadas;
	}

	public List<Zona> getZonasAsociadas() {
		return zonasAsociadas;
	}

	public Integer getCodZonaActual() {
		return codZonaActual;
	}

	public void setCodZonaActual(Integer codZonaActual) {
		this.codZonaActual = codZonaActual;
	}

	public void setCuentas(List<Cuenta> cuentas) {
		this.cuentas = cuentas;
	}

	public List<Cuenta> getCuentas() {
		return cuentas;
	}

	public Date getFecha() {
		return fecha;
	}

	public void setFecha(Date fecha) {
		this.fecha = fecha;
	}

	public Integer getTipoOpSelec() {
		return tipoOpSelec;
	}

	public void setTipoOpSelec(Integer tipoOpSelec) {
		this.tipoOpSelec = tipoOpSelec;
	}

	public List<TipoOperacion> getTipoOperaciones() {
		return tipoOperaciones;
	}

	public void setTipoOperaciones(List<TipoOperacion> tipoOperaciones) {
		this.tipoOperaciones = tipoOperaciones;
	}

	public TipoComprobanteEnum getTipoFactura() {
		return tipoFactura;
	}

	public void setTipoFactura(TipoComprobanteEnum tipoFactura) {
		this.tipoFactura = tipoFactura;
	}

	public String getCuit() {
		return cuit;
	}

	public void setCuit(String cuit) {
		this.cuit = cuit;
	}

	public void setNroFactura1(String nroFactura1) {
		this.nroFactura1 = nroFactura1;
	}

	public String getNroFactura1() {
		return nroFactura1;
	}

	public void setNroFactura2(String nroFactura2) {
		this.nroFactura2 = nroFactura2;
	}

	public String getNroFactura2() {
		return nroFactura2;
	}

	public Double getImporteTotal() {
		return importeTotal;
	}

	public void setImporteTotal(Double importeTotal) {
		this.importeTotal = importeTotal;
	}

	public Double getImporteTotalRestante() {
		return importeTotalRestante;
	}
	
	public Integer getAnioSelec() {
		return anioSelec;
	}

	public void setAnioSelec(Integer anioSelec) {
		this.anioSelec = anioSelec;
	}

	public void setImporteTotalRestante(Double importeTotalRestante) {
		this.importeTotalRestante = importeTotalRestante;
	}
}
